--[[
* MapNotesMininote 2.0
* File: MapNotesMininote\MapNotesMininote.lua
* $Revision: 135 $
* $Date: 2007-01-06 13:13:52 +0000 (Sat, 06 Jan 2007) $
* 
* Copyright (c) 2006-2007, Mery (Moritz Metelmann)
* All rights reserved.
* 
* Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
* 
*    * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
*    * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
*    * Neither the name of the <ORGANIZATION> nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
]]--

-- Check for required AddOns
-- (They are opt.dep because only one is needed to run.)
if (not MapNotes
		and not MapNotesInstance) then return; end;

if (not MapNotesMininote) then MapNotesMininote = {}; end;
if (not MapNotesMininote.Event) then MapNotesMininote.Event = {}; end;
if (not MapNotesMininote.currentMininoteTable) then MapNotesMininote.currentMininoteTable = {}; end;

local L = MapNotesMininote.Localization;

MapNotesMininote.OnLoad = function()
	MapLib.SideMenu.RegisterMenu(L["Mininote"], MapNotesMininoteSideMenu);
	MapLib.EventHandler.RegisterEvent("MAPNOTES_MININOTE", "MININOTELIB_ZONE_CHANGED", MapNotesMininote.Event.MININOTELIB_ZONE_CHANGED);
	MapLib.EventHandler.RegisterEvent("MAPNOTES_MININOTE", "MAPNOTES_NOTE_DELETE", MapNotesMininote.MAPNOTES_NOTE_UPDATE_DELETE);
	MapLib.EventHandler.RegisterEvent("MAPNOTES_MININOTE", "MAPNOTES_NOTE_UPDATE", MapNotesMininote.MAPNOTES_NOTE_UPDATE_DELETE);
	MapLib.EventHandler.RegisterEvent("MAPNOTES_MININOTE", "MAPLIB_UPDATE_MAP", MapNotesMininote.Event.MAPLIB_UPDATE_MAP);
	MininoteLib.Register(MapNotesMininote.currentMininoteTable, true);
	
	-- Find the player model from the Minimap
	-- Its important to unpartent it from the minimap later
	-- or the arrow will stop working when its hidden
	-- this will break other addons hooking in it, but I 
	-- think there are not enough of those to worry about
	local children = {Minimap:GetChildren()};
	local first = false;
	for i=getn(children), 1, -1 do
	  if (children[i]:GetObjectType() == "Model" and not children[i]:GetName() and not first) then
      MapNotesMininote.playerModel = children[i];
      first = true;
	  end;
	end
	-- And while we are at it, up it so its over the Mininotes
	MapNotesMininote.playerModel:SetFrameLevel(3);
end;

MapNotesMininote.Event.MININOTELIB_ZONE_CHANGED = function(newZone)
	MapNotesMininote.UpdateMininoteLibTable(newZone, true);
end;

MapNotesMininote.Event.MAPLIB_UPDATE_MAP = function()
	if (MapNotesMininoteSideMenu:IsVisible()) then
		MapNotesMininote.ScrollBarUpdate();
	end;
end;

MapNotesMininote.MAPNOTES_NOTE_UPDATE_DELETE = function(zoneID, noteID)
	MapNotesMininote.UpdateMininoteLibTable(zoneID);
	if (MapNotesMininoteSideMenu:IsVisible()) then
		MapNotesMininote.ScrollBarUpdate();
	end;
end;

MapNotesMininoteSaved = {};
-- MapNotesMininoteSaved[zoneID] = {
--   [1] = {
--     modID,
--     noteID,
--     isGlobal,
--   };
-- };

MapNotesMininote.ScrollBarUpdate = function()
--	Sea.IO.print("hallo");
  local line; -- 1 through 5 of our window to scroll
  local lineplusoffset; -- an index into our data calculated from the scroll offset
	local curZone = MapLib.mapShown;
	if (MapNotesMininoteSaved[curZone] and MapNotesMininoteSaved[curZone][1]) then
	  FauxScrollFrame_Update(MapNotesMininoteSideMenuScrollBar,getn(MapNotesMininoteSaved[curZone]),7,16); --85 is correct
	  for line=1,7 do
			lineplusoffset = FauxScrollFrame_GetOffset(MapNotesMininoteSideMenuScrollBar) + line;
			if (lineplusoffset <= getn(MapNotesMininoteSaved[curZone])) then
				local title, zone, icon, x, y = MapNotesMininote.GetBasicInfo[MapNotesMininoteSaved[curZone][lineplusoffset].modID](MapNotesMininoteSaved[curZone][lineplusoffset].noteID);
				if (not title) then -- note doesn't exsist
					table.remove(MapNotesMininoteSaved[curZone], lineplusoffset);
					MapNotesMininote.ScrollBarUpdate();
					return;
				end;
				getglobal("MapNotesMininoteSideMenuButton"..line.."Title"):SetText(title);
				getglobal("MapNotesMininoteSideMenuButton"..line):Show();
				local iconTexture = getglobal("MapNotesMininoteSideMenuButton"..line.."Icon");
				iconTexture:SetTexture(icon.file);
				if (icon.tcoordMinX) then iconTexture:SetTexCoord(icon.tcoordMinX, icon.tcoordMaxX, icon.tcoordMinY, icon.tcoordMaxY); else iconTexture:SetTexCoord(0, 1, 0, 1); end;
				if (icon.colorR) then iconTexture:SetVertexColor(icon.colorR, icon.colorG, icon.colorB, icon.colorA); else iconTexture:SetVertexColor(1, 1, 1, 1); end;
				
				-- set the isGlobal Checkbutton (probably shouldn't use checkbuttons, but well)
				local globalbutton = getglobal("MapNotesMininoteSideMenuButton"..line.."GlobalButton");
				if (MapNotesMininoteSaved[curZone][lineplusoffset].isGlobal) then
					globalbutton:SetNormalTexture("Interface\\AddOns\\MapNotesMininote\\Artwork\\Global-Down");
					globalbutton:SetChecked(1);
				else
					globalbutton:SetNormalTexture("Interface\\AddOns\\MapNotesMininote\\Artwork\\Global-Up");
					globalbutton:SetChecked(nil);
				end;
				-- set the isWaypoint Checkbutton (probably shouldn't use checkbuttons, but well)
				local globalbutton = getglobal("MapNotesMininoteSideMenuButton"..line.."WaypointButton");
				if (MapNotesMininoteSaved[curZone][lineplusoffset].isWaypoint) then
					globalbutton:SetNormalTexture("Interface\\AddOns\\MapNotesMininote\\Artwork\\Waypoint-Down");
					globalbutton:SetChecked(1);
				else
					globalbutton:SetNormalTexture("Interface\\AddOns\\MapNotesMininote\\Artwork\\Waypoint-Up");
					globalbutton:SetChecked(nil);
				end;
			else
				getglobal("MapNotesMininoteSideMenuButton"..line):Hide();
			end;
	  end;
	else
		-- hide all buttons
		-- show a warning.
		for line=1,7 do
			getglobal("MapNotesMininoteSideMenuButton"..line):Hide();
	  end;
	end;
end;

MapNotesMininote.SideMenuUpdate = function()
	MapNotesMininote.ScrollBarUpdate();
end;

MapNotesMininote.OnShow = function()
	MapNotesMininote.SideMenuUpdate();
end;

MapNotesMininote.DeleteOnClick = function()
	table.remove(MapNotesMininoteSaved[MapLib.mapShown], FauxScrollFrame_GetOffset(MapNotesMininoteSideMenuScrollBar) + this:GetParent():GetID());
	MapNotesMininote.SideMenuUpdate();
	MapNotesMininote.UpdateMininoteLibTable();
end;

MapNotesMininote.UpdateMininoteLibTable = function(zoneID, noInform)
	zoneID = zoneID or MapLib.GetMapIDByName(GetRealZoneText());
	-- look if table was empty and remeber
	local isEmpty;
	if (table.maxn(MapNotesMininote.currentMininoteTable) == 0) then isEmpty = true; end;
	-- erase the old table, since we already passed the reference, we cannot create a new:
	for k, _ in ipairs(MapNotesMininote.currentMininoteTable) do
		MapNotesMininote.currentMininoteTable[k] = nil;
	end;
	-- build the new table
	-- ckeck for global notes in the other zones still missing
	local hasWaypoint;
	if (MapNotesMininoteSaved[zoneID]) then
		for k, v in ipairs(MapNotesMininoteSaved[zoneID]) do
			local title, zone, icon, x, y = MapNotesMininote.GetBasicInfo[v.modID](v.noteID);
			if (title) then
				local cx, cy, cont = MapLib.ConvertToContinentCoordinates(zone, x, y);
				table.insert(MapNotesMininote.currentMininoteTable, {title = title, icon = icon, x = cx, y = cy});
				-- if its the waypoint --> set it!
				if (v.isWaypoint) then
					-- set x,y,title,icon in the waypoint
					MapNotesMininoteWaypointFrame.x = cx; MapNotesMininoteWaypointFrame.y = cy;
					-- show the waypoint frame
					MapNotesMininoteWaypointFrame:Show();
					hasWaypoint = true;
				end;
			end;
		end;
	end;
	if (not hasWaypoint) then
		-- hide the waypoint frame!
		MapNotesMininoteWaypointFrame:Hide();
	end;
	if (not noInform and ( (table.maxn(MapNotesMininote.currentMininoteTable) == 0 and not isEmpty) or (table.maxn(MapNotesMininote.currentMininoteTable) ~= 0 and isEmpty) ) ) then
	-- inform mininotelib if the table size changed to/from 0 elements to reevaluate if the onupdate has to be shown.
		MapLib.EventHandler.TriggerEvent("MININOTELIB_INFORM_TABLE_CHANGED");
	end;
end;

-- also need a function that reacts to note update
-- and not delete events from MapNotes.

MapNotesMininote.GetMenuName = function(poi)
	-- poi.modID, poi.id
	-- check if it is already in the list:
	if (MapNotesMininoteSaved[MapLib.mapShown]) then
		for _, noteInfo in pairs(MapNotesMininoteSaved[MapLib.mapShown]) do
			if (noteInfo.modID == poi.modID and noteInfo.noteID == poi.id) then
				if (noteInfo.isWaypoint) then
					return L["Unset Mininote & Waypoint"];
				else
					return L["Unset Mininote"]; -- "Unset Mininote & Waypoint"
				end;
			end;
		end;
	end;
	return L["Set Mininote"];
end;

MapNotesMininote.GetMenuWaypointName = function(poi)
	if (MapNotesMininoteSaved[MapLib.mapShown]) then
		for _, noteInfo in pairs(MapNotesMininoteSaved[MapLib.mapShown]) do
			if (noteInfo.modID == poi.modID and noteInfo.noteID == poi.id) then
				if (noteInfo.isWaypoint) then
					return L["Unset Waypoint"]; -- "Unset Mininote & Waypoint"
				else
					return L["Set Waypoint"];
				end;
			end;
		end;
	end;
	return L["Set Mininote & Waypoint"];
end;

MapNotesMininote.ToggleNote = function(poi)
	if (MapNotesMininoteSaved[MapLib.mapShown]) then
		for i, noteInfo in pairs(MapNotesMininoteSaved[MapLib.mapShown]) do
			if (noteInfo.modID == poi.modID and noteInfo.noteID == poi.id) then
				table.remove(MapNotesMininoteSaved[MapLib.mapShown], i);
				MapNotesMininote.SideMenuUpdate();
				MapNotesMininote.UpdateMininoteLibTable();
				return;
			end;
		end;
	else
		MapNotesMininoteSaved[MapLib.mapShown] = {};
	end;	
	table.insert(MapNotesMininoteSaved[MapLib.mapShown], {modID = poi.modID, noteID = poi.id});
	table.sort(MapNotesMininoteSaved[MapLib.mapShown], MapNotesMininote.CompareFunction);
	MapNotesMininote.SideMenuUpdate();
	MapNotesMininote.UpdateMininoteLibTable();
end;

MapNotesMininote.ToggleWaypoint = function(poi)
	local isAlreadyMininote;
	if (MapNotesMininoteSaved[MapLib.mapShown]) then
		for i, noteInfo in pairs(MapNotesMininoteSaved[MapLib.mapShown]) do
			if (noteInfo.modID == poi.modID and noteInfo.noteID == poi.id) then
				isAlreadyMininote = true;
				if (noteInfo.isWaypoint) then
					noteInfo.isWaypoint = nil;
				else
					noteInfo.isWaypoint = true;
				end;
			else
				noteInfo.isWaypoint = nil;
			end;
		end;
	else
		MapNotesMininoteSaved[MapLib.mapShown] = {};
	end;
	if (not isAlreadyMininote) then
		table.insert(MapNotesMininoteSaved[MapLib.mapShown], {modID = poi.modID, noteID = poi.id, isWaypoint = true;});
		table.sort(MapNotesMininoteSaved[MapLib.mapShown], MapNotesMininote.CompareFunction);
	end;
	MapNotesMininote.SideMenuUpdate();
	MapNotesMininote.UpdateMininoteLibTable();
end;

MapNotesMininote.ToggleWaypoint_SideMenu = function(this)
	for i, noteInfo in ipairs(MapNotesMininoteSaved[MapLib.mapShown]) do
		if (i == this:GetID() + FauxScrollFrame_GetOffset(MapNotesMininoteSideMenuScrollBar)) then
			if (noteInfo.isWaypoint) then
				noteInfo.isWaypoint = nil;
			else
				noteInfo.isWaypoint = true;
			end;
		else
			noteInfo.isWaypoint = nil;
		end;
	end;
	MapNotesMininote.SideMenuUpdate();
	MapNotesMininote.UpdateMininoteLibTable();
end;

MapNotesMininote.CompareFunction = function(subt1, subt2)
	local title1 = MapNotesMininote.GetBasicInfo[subt1.modID](subt1.noteID) or "";
	local title2 = MapNotesMininote.GetBasicInfo[subt2.modID](subt2.noteID) or "";
	if (string.lower(title1) < string.lower(title2)) then
		return true;
	else
		return false;
	end;
end;

local timepassed = 0;
local count_frame = 0;
MapNotesMininote.DELAY_BETWEEN_ARROW_UPDATES = 0.04;
MapNotesMininote.WaypointFrame_OnUpdate = function(this, arg1)
  timepassed = timepassed + arg1;
  if (timepassed > MapNotesMininote.DELAY_BETWEEN_ARROW_UPDATES) then
    local player_x, player_y, continentID = MapLib.GetPlayerContinentPosition();
		if (player_x ~= 0 and player_y ~= 0 and MapNotesMininote.playerModel:IsVisible()) then
      local currentZoom = Minimap:GetZoom();
      local dx = (this.x - player_x) * MininoteLib.Conversion[continentID][0].xscale;
      local dy = (this.y - player_y) * MininoteLib.Conversion[continentID][0].yscale;
			if (sqrt(dx * dx + dy * dy) > 1.2) then
				local delta_x = (this.x - player_x) * MininoteLib.Conversion[continentID][currentZoom].xscale * (MininoteLib.isInside and MininoteLib.Conversion["isInside"][currentZoom] or 1);
				local delta_y = (this.y - player_y) * MininoteLib.Conversion[continentID][currentZoom].yscale * (MininoteLib.isInside and MininoteLib.Conversion["isInside"][currentZoom] or 1);
	      local rad_angle = math.atan2(delta_y, delta_x);
	      local imageIndex = math.fmod(math.floor((-MapNotesMininote.playerModel:GetFacing() - rad_angle - math.rad(90))* 17.188733853924696263039446444232)+ 216, 108)
		    local col, row = math.fmod(imageIndex, 9), math.floor(imageIndex/9);
		    MapNotesMininoteWaypointArrow:SetTexCoord(col*56/512, (col+1)*56/512, row*42/512, (row+1)*42/512);
		    MapNotesMininoteWaypointArrow:Show();
		    MapNotesMininoteWaypointArrowUp:Hide();
			else
		    local col, row = math.fmod(count_frame, 9), math.floor(count_frame/9);
		    MapNotesMininoteWaypointArrowUp:SetTexCoord(col*53/512, (col+1)*53/512, row*70/512, (row+1)*70/512);
		    MapNotesMininoteWaypointArrowUp:Show();
		    MapNotesMininoteWaypointArrow:Hide();
		    count_frame = math.fmod(count_frame + 1, 54);
			end;
    	timepassed = 0;
    else
    	MapNotesMininoteWaypointArrow:Hide();
    	MapNotesMininoteWaypointArrowUp:Hide();
    end;
  end;
end;

if (MapNotesInstance) then MapLib.POI.RegisterMenuButton(MapNotesMininote.GetMenuName, MapNotesMininote.ToggleNote, {"MAPNOTES", "MN_INSTANCE"}, function() return true; end); end;
if (MapNotes) then MapLib.POI.RegisterMenuButton(MapNotesMininote.GetMenuWaypointName, MapNotesMininote.ToggleWaypoint, {"MAPNOTES", "MN_INSTANCE"}, function() return true; end); end;

-- Because if this it is _very_ improtant to load MapNotes Mininote after all mods that get used:
MapNotesMininote.GetBasicInfo = {
	["MAPNOTES"] = function(noteID) if (MapNotes) then return MapNotes.GetBasicNoteInfo(noteID) else return end end,
	["MN_INSTANCE"] = function(noteID) if (MapNotesInstance) then return MapNotesInstance.GetBasicInstancePortalInfo(noteID) else return end end,
}